from skillchallenge.models import *
from django.db.models import Q
import EventEngine

class CodeEngine:
    
    def execute(self, action, char=None, debug=False):
        if debug:
            print action
        result = None
        exec(action.code)
        return result
    
    def preconditions_met(self, preconditions, debug=False):
        context = globals()
        context['fired'] = EventEngine.EventEngine().fired()
        context['now'] = Tick.objects.latest()
        import ChatEngine
        context['global_chat_engine'] = ChatEngine.ChatEngine()
        import SkillChallengeEngine
        context['global_skillchallenge_engine'] = SkillChallengeEngine.SkillChallengeEngine()
        import Quest
        context['global_quest_engine'] = Quest.Quest()
        if debug: print 'NOOOWWW ==========> ' + str(context['now'])
        now = Tick.objects.latest()
        result = True
        break_on_true = False
        for precon in preconditions:
            break_on_true = False
            for evaluation in precon.code_array:
                if debug: print 'eval >>> ' + evaluation + '\n'
                if evaluation.startswith('break'):
                    evaluation = evaluation.split(':')[1]
                    break_on_true = True
                try:
#                    if debug: print 'pre eval >>> ' + evaluation
                    result = result and eval(evaluation)
                    if debug: print 'resultado >>> ' + str(result) + '\n'
                    if ((not result) and (not break_on_true)) or (break_on_true and result):
                        break
                    else:
                        break_on_true = False
                except SyntaxError:
                    exec(evaluation)
            if not result:
                break
        return result
    
    def create_message_action(self, message, description = None, tags = None):
        if not description:
            description = message
        return Code.objects.create(desc = description, tags = tags, 
                 code = 'Message.objects.create(text="' + message + '")')
    
    def create_fire_event_if_ok_action(self, event, description =None, tags = None):
        if not description:
            description  = 'create and fire: ' + str(event)
        c = Code(desc = description, tags = tags) 
        c.code_array = ['event = Event.objects.get(id=' + str(event.id) + ')',
                      'event.preconditions = [precon for precon in event.preconditions.all()[1:]]',
                      'result = event']
        c.save()
        return c
    
    def engage_engine_action(self, engine, event_id, object_id, description=None, tags=None):
        if not description:
            description = 'create engagement for ' + engine + ' and ' + str(object_id)
        c = Code(desc = description, tags=tags)
        c.code_array = [engine + '.engage(' + str(event_id) + ',' + str(object_id) + ',self.__get_id__(char))']
        c.save()
        return c
    
    def __get_id__(self, char):
        if char:
            return char.id
    
    def assert_is_message(self, object, message):
        return ((type(object) == Code) and 
                (object.code_array[0] == ('Message.objects.create(text="' + message + '")')))
    
    def create_event_precon(self, q, description = None, tags = None):
        if not description:
            description = str(q)
        c = Code(desc = description, tags = tags)
        c.code_array = [self.__create_q_string__(q), 
                        'Event.objects.filter(query).filter(timestamp__isnull=False).count() > 0']
        c.save()
        return c
    
    def create_latest_event_precon(self, setq, q, negate=False, description=None, tags=None):
        if not description:
            description = str(setq) + " > " + str(q)
        c = Code(desc = description, tags = tags)
        code_array = [self.__create_q_string__(setq), 
                        'events = Event.objects.filter(query)']
        
        if negate:
            code_array.append('break:len(events) == 0')
        else:
            code_array.append('len(events) > 0')
            
        code_array.append('latest = events.latest()')
        code_array.append('latest != None')
        code_array.append(self.__create_q_string__(q))
        
        if negate:
            code_array.append('break:len(events.filter(query)) == 0')
            code_array.append('latest not in events.filter(query)')
        else:
            code_array.append('len(events.filter(query)) > 0')
            code_array.append('latest in events.filter(query)')
            
        c.code_array = code_array
        c.save()
        return c
    
    def create_compare_event_precon(self, q1, q2, property, description=None, tags=None):
        if not description:
            description = str(q1) + " == " + str(q2)
        c = Code(desc = description, tags = tags)
        c.code_array = [self.__create_q_string__(q1),
                      'events = Event.objects.filter(query)',
                      'len(events) > 0',
                      'latest1 = events.latest()',
                      'latest1 != None',
                      self.__create_q_string__(q2),
                      'events = Event.objects.filter(query)',
                      'len(events) > 0',
                      'latest2 = events.latest()',
                      'latest2 != None',
                      'latest1.' + property + ' == latest2.' + property,
                      ]
        c.save()
        return c
    
    def create_skill_test_precon(self, skill_test, success, description=None, tags=None):
        if not description:
            description = 'skill test for ' + str(skill_test) + ' | condition : ' + str(success)
            
        c = Code(desc = description, tags = tags)
        c.code_array = ['SkillTest.objects.get(id=' + str(skill_test.id) + ').complete == ' + str(success)]
        c.save()
        return c
    
    def create_skill_challenge_precon(self, skill_challenge, success, description=None, tags=None):
        if not description:
            description = 'skill challenge for ' + str(skill_challenge) + ' | condition : ' + str(success)
            
        c = Code(desc = description, tags = tags)
        if success:
            c.code_array = ['SkillChallenge.objects.get(id=' + str(skill_challenge.id) + ').complete == True']
        else:
            c.code_array = ['SkillChallenge.objects.get(id=' + str(skill_challenge.id) + ').failed == True']
        c.save()
        return c

    def create_quest_precon(self, quest, success_condition, description=None, tags=None):
        if not description:
            description = 'quest precon for ' + str(quest) + ' | condition : ' + str(success_condition)
            
        c = Code(desc = description, tags = tags)
        if success_condition == None:
            c.code_array = ['QuestModel.objects.get(id=' + str(quest.id) + ').holder != None',
                            'not QuestModel.objects.get(id=' + str(quest.id) + ').is_complete()']
        else:
            if not success_condition:
                c.code_array = ['QuestModel.objects.get(id=' + str(quest.id) + ').failed']
            else:
                c.code_array = ['QuestModel.objects.get(id=' + str(quest.id) + ').success']
        c.save()
        return c
    
    def create_negate_by_info(self, info, description=None, tags=None):
        if not description:
            description = 'NEGATE > ' + info
        c = Code(desc = description, tags = tags)
        c.code_array = ['fired.filter(info__icontains = "' + info + '").count() == 0']
        c.save()
        return c
    
    def create_always_fail_precon(self, description=None, tags=None):
        if not description:
            description = 'Always Fail'
        c = Code(desc = description, tags = tags)
        c.code_array = ['1 == 0']
        c.save()
        return c
        
    
    def assert_event_precon(self, precon, q):
        return ((type(precon) == Code) and
                (len(precon.code_array) == 2) and
                (precon.code_array[0] == self.__create_q_string__(q)) and
                (precon.code_array[1] == 'Event.objects.filter(query).count() > 0'))
        
    def assert_is_negate_by_info(self, precon, info):
        return (type(precon) == Code) and \
                (len(precon.code_array) == 1) and \
                (precon.code_array[0] == ('fired.filter(info__icontains = "' + info + '").count() == 0'))
                
    def __raw_q_string__(self, children):
        if children:
            if type(children) == str:
                return children
            else:
                return 'Q(' + '='.join([str(x) for x in children]) + ')'
        else:
            return None
    
    def __q_string__(self, q):
        if type(q) != Q:
            return self.__raw_q_string__(q)
        join = None
        if q.connector == 'AND':
            join = '&'
        else:
            join = '|'
            
        return '(' + join.join(self.__q_string__(x) for x in q.children) + ')'
    
    def __create_q_string__(self, q): 
        return 'query = ' + str(self.__q_string__(q))